#include "copyright.h"
#define _REAL_ double precision

subroutine chkfrc(xx,ix,ih,ipairs,x,fg,ene,r_stack,i_stack, &
              atm1,atm2,nrotx,ntrns,msph,delta)

   use poisson_boltzmann, only: savh, nfocus

   implicit none

#  include "pb_constants.h"
#  include "../lib/random.h"
#  include "md.h"
#  include "pb_md.h"
#  include "memory.h"

   ! Passed
   integer atm1, atm2, nrotx, ntrns, msph
   integer ix(*), ipairs(*), i_stack(*)
   character(len=4) ih(*)
   _REAL_ delta
   _REAL_ xx(*), x(*), fg(*)
   _REAL_ ene(51), r_stack(*)

   ! Local
   integer i, j, k, l, m, n, s
   integer ncrcl, nfopt, n_force_calls
   _REAL_ theta(msph), psi(msph)
   _REAL_ dsplc(3), rtn_c(3), rtn_s(3)
   _REAL_ crd(3*natom), xcrd(3*natom), ycrd(3*natom), zcrd(3*natom)
   _REAL_ f(3*natom), nf(3*natom)
   _REAL_ tf(3*natom), tfsq(3*natom)
   _REAL_ tnf(3*natom), tnfsq(3*natom)
   _REAL_ h, sxangle, xangle, randval, vir(4), enep, enem, rms
   _REAL_ aene(51)
   _REAL_ prob
   logical do_list_update
   type (rand_gen_state) :: rand_gen
   _REAL_ crn(3,20), crn1(3), crn2(3)

   if ( nfocus /= 1 ) then
      print *, "nfocus must be 1"
      call mexit(6,0)
   end if

   ! initialization
   prob = 1.5d0
   do_list_update = .true.
   h = savh(nfocus)
   vir = ZERO
   nf = ZERO
   tf = ZERO; tfsq = ZERO
   tnf = ZERO; tnfsq = ZERO
   n_force_calls = 0
   rms = ZERO
   crd(1:3*natom) = x(1:3*natom)
   call amrset_gen(rand_gen,ig)
   call crcl(ncrcl,msph,theta,psi)   

!  do l = 1, ncrcl
!     write(400,'(2f20.10)') theta(l), psi(l)
!  end do

   ! let the main axis coincide/parallel with z axis
!  m = 3*(atm1-1)
!  dsplc = -crd(m+1:m+3)
!  call translate(natom,crd,dsplc)
!  m = 3*(atm2-1)
!  n = 3*(atm1-1)
!  rtn_c = crd(m+1:m+3) - crd(n+1:n+3)
!  call sphe(rtn_c,rtn_s)
   rtn_s = ZERO
!  call rotate(natom,crd,3,-rtn_s(3))
!  call rotate(natom,crd,2,-rtn_s(2))

   ! rotate wrt the main axis (z axis)
   sxangle = TWOPI/nrotx
   xangle = ZERO
   do l = 1, nrotx
      xcrd = crd
      if ( l > 1 ) then
         xangle = (l-1)*sxangle
         call rotate(natom,xcrd,3,xangle)
      end if
    
      s = 1
!     if (l == 2) s = ncrcl
      do k = s, ncrcl   
         ycrd = xcrd

         !rotate the main axis
         call rotate(natom,ycrd,2,theta(k))
         call rotate(natom,ycrd,3,psi(k))
        
         !translate the molecule
         do j = 1, ntrns
            do i = 1, 3
               call amrand_gen(rand_gen,randval)
               dsplc(i) = (randval-HALF)*h
            end do
!           dsplc = ZERO
            zcrd = ycrd
            call translate(natom,zcrd,dsplc)
            x(1:3*natom) = zcrd(1:3*natom)
            fg(1:3*natom) = ZERO
            
!           write(300,*) '--------------------------------'
!           write(300,*) 'rotation',l,k,'translation',j
!           write(300,*) natom
!           do n = 1, natom-1, 2
!              m = 3*(n-1)
!              write(300,'(6f12.7)') zcrd(m+1),zcrd(m+2),zcrd(m+3),zcrd(m+4),zcrd(m+5),zcrd(m+6)
!           end do
!           if ( natom == n ) then
!              m = 3*(n-1)
!              write(300,'(3f12.7)') zcrd(m+1),zcrd(m+2),zcrd(m+3)
!           end if
      
!           do n = 1, natom
!              m = 3*(n-1)
!              write(301,'(3f20.15)') zcrd(m+1),zcrd(m+2),zcrd(m+3)
!           end do

            ! analytical force
            ene = ZERO
            pbgrid = .true.
            call force(xx,ix,ih,ipairs,x,fg,ene,vir,r_stack,i_stack, &
                   xx(l96),xx(l97),xx(l98),do_list_update)
            aene = ene

!           do m = 1, natom
!              n=(m-1)*3
!              write(555,'(3f20.10)') fg(n+1),fg(n+2),fg(n+3)
!           end do

            ! numerical force
            nfopt = 0
            if ( nfopt == 1 ) then
               ! only for cg test
               do n = 1, 3
!                 print *, l, k, j, n
                  x(1:3*natom) = zcrd(1:3*natom)
                  do m = 20, 35
                     i = (m-1)*3+n
                     x(i) = x(i) + delta
                  end do
                  ene = ZERO
                  pbgrid = .true.
                  call force(xx,ix,ih,ipairs,x,f,ene(23),vir,r_stack,i_stack, &
                          xx(l96),xx(l97),xx(l98),do_list_update)
                  enep = ene(23)
                  x(1:3*natom) = zcrd(1:3*natom)
                  do m = 20, 35
                     i = (m-1)*3+n
                     x(i) = x(i) - delta
                  end do
                  ene = ZERO
                  pbgrid = .true.
                  call force(xx,ix,ih,ipairs,x,f,ene(23),vir,r_stack,i_stack, &
                          xx(l96),xx(l97),xx(l98),do_list_update)
                  enem = ene(23)
                  nf(n) = -(enep-enem)/(TWO*delta) 
               end do
            elseif ( nfopt == 2 ) then 
               do m = 1, natom
                  do n = 1, 3
!                    print *, l, k, j, m, n
                     i = (m-1)*3+n
                     x(1:3*natom) = zcrd(1:3*natom)
                     x(i) = x(i) + delta
                     ene = ZERO
                     pbgrid = .true.
                     call force(xx,ix,ih,ipairs,x,f,ene(23),vir,r_stack,i_stack, &
                             xx(l96),xx(l97),xx(l98),do_list_update)
                     enep = ene(23)
                     x(1:3*natom) = zcrd(1:3*natom)
                     x(i) = x(i) - delta
                     ene = ZERO
                     pbgrid = .true.
                     call force(xx,ix,ih,ipairs,x,f,ene(23),vir,r_stack,i_stack, &
                             xx(l96),xx(l97),xx(l98),do_list_update)
                     enem = ene(23)
                     nf(i) = -(enep-enem)/(TWO*delta) 
                  end do
               end do
            else 
            end if

            ! rotate back force
            call rotate(natom,fg,3,-psi(k))
            call rotate(natom,fg,2,-theta(k))
            call rotate(natom,fg,3,-xangle)
            call rotate(natom,fg,2,rtn_s(2))
            call rotate(natom,fg,3,rtn_s(3))
            if ( nfopt /= 0 ) then
               call rotate(natom,nf,3,-psi(k))
               call rotate(natom,nf,2,-theta(k))
               call rotate(natom,nf,3,-xangle)
               call rotate(natom,nf,2,rtn_s(2))
               call rotate(natom,nf,3,rtn_s(3))
            end if

            ! print out force
!           write(6,'(3f20.10)') dsplc(1),dsplc(2),dsplc(3)
!           write(6,*) zcrd(1:3*natom)
            call writefile(natom,l,k,j,fg,nf, &
                           tf,tfsq,tnf,tnfsq, &
                           nfopt)

            ! print out energy
            n_force_calls = n_force_calls + 1
            call report_min_progress( n_force_calls, rms, fg, aene, ih(m04) )  
         end do
      end do
   end do

   call writesummary(natom,nrotx,ncrcl,ntrns, &
                     tf,tfsq,tnf,tnfsq, &
                     nfopt)

end subroutine chkfrc

subroutine sphe(car,sph)

   implicit none

#  include "pb_constants.h"

   _REAL_ car(3), sph(3)

   sph(1) = sqrt(car(1)**2+car(2)**2+car(3)**2)
   sph(2) = acos(car(3)/sph(1))
   if ( car(1) == 0 ) then
      sph(3) = HALF*PI
   else
      sph(3) = atan(abs(car(2)/car(1)))
   end if

   if ( car(1) >= 0 ) then
      if ( car(2) >= 0 ) then
         sph(3) = sph(3)
      else
         sph(3) = TWOPI - sph(3)
      end if
   else
      if ( car(2) >= 0 ) then
         sph(3) = PI - sph(3)
      else
         sph(3) = PI + sph(3)
      end if
   end if

end subroutine sphe

subroutine translate(n,car,dsplc)

   implicit none

   integer n
   _REAL_ car(3,n), dsplc(3)

   car(1,1:n) = car(1,1:n) + dsplc(1)
   car(2,1:n) = car(2,1:n) + dsplc(2)
   car(3,1:n) = car(3,1:n) + dsplc(3)

end subroutine translate

subroutine rotate(n,car,axis,angle)

   implicit none

#  include "pb_constants.h"

   integer i, n, axis
   _REAL_ car(3,n), angle
   _REAL_ R(3,3), tmp(3)

   ! Warning eliminator
   R = ZERO

    if ( axis == 1 ) then
       R(1,1:3) = (/ONE,ZERO,ZERO/)
       R(2,1:3) = (/ZERO,cos(angle),-sin(angle)/)
       R(3,1:3) = (/ZERO,sin(angle),cos(angle)/)
    else if ( axis == 2 ) then
       R(1,1:3) = (/cos(angle),ZERO,sin(angle)/)
       R(2,1:3) = (/ZERO,ONE,ZERO/)
       R(3,1:3) = (/-sin(angle),ZERO,cos(angle)/)
    else if ( axis == 3 ) then
       R(1,1:3) = (/cos(angle),-sin(angle),ZERO/)
       R(2,1:3) = (/sin(angle),cos(angle),ZERO/)
       R(3,1:3) = (/ZERO,ZERO,ONE/)
    else
       print *, "rotation axis error"
       call mexit(6,0)
    end if

    do i = 1, n
       tmp = car(1:3,i)
       car(1,i) = R(1,1)*tmp(1)+R(1,2)*tmp(2)+R(1,3)*tmp(3)
       car(2,i) = R(2,1)*tmp(1)+R(2,2)*tmp(2)+R(2,3)*tmp(3)
       car(3,i) = R(3,1)*tmp(1)+R(3,2)*tmp(2)+R(3,3)*tmp(3)
    end do

end subroutine rotate

subroutine crcl(n,maxsph,theta,psi)

   implicit none

#  include "pb_constants.h"

   ! Passed variables

   integer n, maxsph
   _REAL_ theta(maxsph), psi(maxsph)

   ! Local variables

   integer ntheta,npsi,npsimax,i,nt,np
   _REAL_ thtstp,tht,stheta,psistp

   ! begin code

   ntheta = int(sqrt(PI*maxsph/FOUR))
   npsimax = int(TWO*ntheta)
   thtstp = PI/ntheta

   i = 1
   do nt = 1, ntheta
      tht = thtstp*nt
      stheta = sin(tht)
      npsi = nint(stheta*npsimax)
      if (npsi == 0) cycle
      psistp = TWOPI/npsi
      do np = 1, npsi
         psi(i) = np*psistp
         theta(i) = tht
         i = i + 1
      enddo
   enddo
   n = i - 1

end subroutine crcl

subroutine writefile(natom,l,k,j,fg,nf, &
                     tf,tfsq,tnf,tnfsq, &
                     nfopt)

   implicit none

   integer l, k, j
   integer m, natom, nfopt
   _REAL_ fg(3,natom), nf(3,natom)
   _REAL_ tf(3,natom), tfsq(3,natom)
   _REAL_ tnf(3,natom), tnfsq(3,natom)
   _REAL_ tf1(3), tnf1(3)

   tf1(1) = sum(fg(1,1:natom))
   tf1(2) = sum(fg(2,1:natom))
   tf1(3) = sum(fg(3,1:natom))
   tnf1(1) = sum(nf(1,1:natom))
   tnf1(2) = sum(nf(2,1:natom))
   tnf1(3) = sum(nf(3,1:natom))

   tf = tf + fg
   tfsq = tfsq+fg*fg
   tnf = tnf + nf 
   tnfsq = tnfsq+nf*nf
   
   write(6,*) '--------------------------------'
   write(6,*) 'rotation',l,k,'translation',j
   write(6,*) 'analytical force'
   do m = 1, natom
      write(6,'(3f20.10)') fg(1,m),fg(2,m),fg(3,m)
   end do
   write(6,*) 'total analytical force'
   write(6,'(3f20.10)') tf1(1),tf1(2),tf1(3)

   if ( nfopt /= 0 ) then
      write(6,*) 'numerical force'
      do m = 1, natom
         write(6,'(3f20.10)') nf(1,m),nf(2,m),nf(3,m)
      end do
      write(6,*) 'total numerical force'
      write(6,'(3f20.10)') tnf1(1),tnf1(2),tnf1(3)
   end if

end subroutine writefile

subroutine writesummary(natom,nrotx,ncrcl,ntrns, &
                        tf,tfsq,tnf,tnfsq, &
                        nfopt)
   implicit none

   integer natom,nrotx,ncrcl,ntrns
   _REAL_ tf(3,natom), tfsq(3,natom)
   _REAL_ tnf(3,natom), tnfsq(3,natom)
   integer nfopt

   integer m, nort
   _REAL_ wt

   nort = nrotx*ncrcl*ntrns
   write(6,*) "Summary of finite-difference force"
   write(6,*) "Number of orientations:  ",nort

   wt = 1.d0/dble(nort)
   tf = tf*wt
   tfsq = sqrt(tfsq*wt-tf*tf)
   write(6,*) "Mean of analytical force"
   do m = 1, natom
      write(6,'(3f20.10)') tf(1,m),tf(2,m),tf(3,m)
   end do
   write(6,*) "Standard deviation of analytical force"
   do m = 1, natom
      write(6,'(3f20.10)') tfsq(1,m),tfsq(2,m),tfsq(3,m)
   end do
   
   if ( nfopt /= 0 ) then
      tnf = tnf*wt
      tnfsq = sqrt(tnfsq*wt-tnf*tnf)
      write(6,*) "Mean of numerical force"
      do m = 1, natom
         write(6,'(3f20.10)') tnf(1,m),tnf(2,m),tnf(3,m)
      end do
      write(6,*) "Standard deviation of numerical force"
      do m = 1, natom
         write(6,'(3f20.10)') tnfsq(1,m),tnfsq(2,m),tnfsq(3,m)
      end do
   end if

end subroutine writesummary

subroutine get_arccrn(natom,crd,r,idx,prob,crn1,crn2)

   implicit none

#  include "pb_constants.h"

   ! Passed
   integer natom, idx(3)
   _REAL_ crd(3,natom) 
   _REAL_ prob, r(natom)
   _REAL_ crn1(3), crn2(3)

   ! Local
   integer m, n, mp, np, j
   _REAL_ a1(3), a2(3), a3(3)
   _REAL_ p1(3), p2(3), q1(3), q2(3), p(3), d(3)
   _REAL_ u(3,3), v(3,3), w(3), b(3), t(3)
   _REAL_ TOL, wmax, thresh, h
 
   a1 = crd(1:3,idx(1)); a2 = crd(1:3,idx(2)); a3 = crd(1:3,idx(3))
   p1 = a2 - a1; p2 = a3 - a1
   q1 = (a1+a2)*HALF; q2 = (a1+a3)*HALF
   d(1) = r(idx(1)) + prob
   d(2) = r(idx(2)) + prob
   d(3) = r(idx(3)) + prob
   p(1) = p1(2)*p2(3) - p2(2)*p1(3)
   p(2) = p2(1)*p1(3) - p1(1)*p2(3)
   p(3) = p1(1)*p2(2) - p2(1)*p1(2)
   b(1) = dot_product(p1,q1) + (d(1)+d(2))*(d(1)-d(2))*HALF
   b(2) = dot_product(p2,q2) + (d(1)+d(3))*(d(1)-d(3))*HALF
   b(3) = dot_product(p,a1)
   u(1,1:3) = p1
   u(2,1:3) = p2
   u(3,1:3) = p
!  print *, a1(1), a1(2), a1(3)
!  print *, a2(1), a2(2), a2(3)
!  print *, a3(1), a3(2), a3(3)
!  print *, p1(1), p1(2), p1(3)
!  print *, d(1), d(2), d(3)
!  print *, "u1",u(1,1), u(2,1), u(3,1)
!  print *, "u2",u(1,2), u(2,2), u(3,2)
!  print *, "u3",u(1,3), u(2,3), u(3,3)
!  print *, "b",b(1), b(2), b(3)
  
   mp = 3; np = 3
   m = 3; n = 3
   TOL = 1.d-5
   call svdcmp(u,m,n,mp,np,w,v)
   wmax = ZERO
   do j = 1, n
     if ( w(j) > wmax ) wmax = w(j)
   end do
   thresh = TOL * wmax
   do j = 1, n
     if ( w(j) < thresh ) w(j) = ZERO
   end do
   call svbksb(u,w,v,m,n,mp,np,b,t)
!  print *, "t",t(1), t(2), t(3)

   p = p/sqrt(p(1)**2+p(2)**2+p(3)**2)
   h = sqrt(d(1)**2-dot_product(t-a1,t-a1))
!  print *,d(1),h
!  print *,t(1)-a1(1),t(2)-a1(2),t(3)-a1(3)
!  h = sqrt(d(2)**2-dot_product(t-a2,t-a2))
!  print *,h
!  h = sqrt(d(3)**2-dot_product(t-a3,t-a3))
!  print *,h
   crn1 = t + h*p
   crn2 = t - h*p
!  print *, "c1",crn1(1), crn1(2), crn1(3)
!  print *, "c2",crn2(1), crn2(2), crn2(3)

end subroutine get_arccrn
